package com.dd.ms.auth.service.impl;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.dd.ms.auth.mapper.UserMapper;
import com.dd.ms.auth.pojo.DO.RefreshTokenDO;
import com.dd.ms.auth.pojo.DO.RoleDO;
import com.dd.ms.auth.pojo.DO.UserDO;
import com.dd.ms.auth.pojo.DO.UserRolesRelDO;
import com.dd.ms.auth.pojo.dto.RolePermissionDTO;
import com.dd.ms.auth.pojo.enumeration.UserStatusEnum;
import com.dd.ms.auth.pojo.resp.AuthResponse;
import com.dd.ms.auth.pojo.resp.RefreshTokenResponse;
import com.dd.ms.auth.repository.*;
import com.dd.ms.auth.vo.RoleVO;
import com.dd.ms.auth.req.LoginReq;
import com.dd.ms.auth.req.RegisterReq;
import com.dd.ms.auth.service.AuthService;
import com.dd.ms.auth.config.auth.JwtUtils;
import com.dd.ms.auth.utils.converter.PermissionConverter;
import com.dd.ms.auth.utils.converter.RoleConverter;
import com.dd.ms.common.exception.DomainException;
import lombok.RequiredArgsConstructor;
import org.apache.commons.codec.digest.DigestUtils;
import org.springframework.http.HttpStatus;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import javax.servlet.http.HttpServletRequest;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @Author liuxianmeng
 * @CreateTime 2025/7/9 10:16
 * @Description 类功能作用说明
 */
@Service
@RequiredArgsConstructor
public class AuthServiceImpl extends ServiceImpl<UserMapper, UserDO> implements AuthService {

    private static final String DEFAULT_ROLE = "USER";

    private final JwtUtils jwtUtils;
    private final UserRepository userRepository;
    private final UserRolesRelRepository userRolesRelRepository;
    private final RolePermissionRelRepository rolePermissionRelRepository;
    private final PasswordEncoder passwordEncoder;
    //private final TokenBlacklistService tokenBlacklistService;
    private final RoleRepository roleRepository;
    private final RefreshTokenRepository refreshTokenRepository;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public AuthResponse register(RegisterReq req) {
        if (userRepository.userExists(req.getAccount())) {
            throw new DomainException(HttpStatus.CONFLICT.value(), "该用户名已存在");
        }

        UserDO user = UserDO.builder()
                .account(req.getAccount())
                .password(passwordEncoder.encode(req.getPassword()))
                .userRealName(req.getUserRealName())
                .status(UserStatusEnum.ACTIVE.getCode())
                // 默认写死
                .userAvatar("https://bigbigmeng-cd-f-1317734527.cos.ap-chengdu.myqcloud.com" +
                        "/dd-openapi/202507/f33116bd-292b-4014-9f11-7ef92190ca72.jpg?")
                .build();
        userRepository.save(user);

        // 分配默认角色
        assignDefaultRole(user.getUserId());

        // 重新查询实际角色列表（避免硬编码）
        List<String> roleNames = getUserRolesWithPermissions(user.getUserId())
                .stream().map(RoleVO::getRoleName).collect(Collectors.toList());

        // 生成令牌（包含用户ID）
        String accessToken = jwtUtils.generateAccessToken(user.getAccount(), roleNames);
        String refreshToken = jwtUtils.generateRefreshToken(user.getAccount());

        return AuthResponse.builder()
                .accessToken(accessToken).refreshToken(refreshToken)
                .userAvatar("https://gw.alipayobjects.com/zos/antfincdn/XAosXuNZyF/BiazfanxmamNRoxxVxka.png")
                .expiresIn(jwtUtils.getExpirationFromToken(accessToken))
                .tokenType("Bearer").account(user.getAccount())
                .build();
    }

    @Override
    public AuthResponse login(LoginReq req, HttpServletRequest request) {

        // 1. 检查用户是否存在
        UserDO user = userRepository.getUserByAccount(req.getAccount());
        if (user == null) {
            throw new DomainException(401, "账号或密码错误"); // 模糊提示防止枚举攻击
        }

        // 2. 手动验证密码
        if (!passwordEncoder.matches(req.getPassword(), user.getPassword())) {
            throw new DomainException(401, "账号或密码错误");
        }

        // 3. 检查账户状态
        if (!UserStatusEnum.ACTIVE.getCode().equals(user.getStatus())) {
            throw new DomainException(401, "此账户当前不可用，状态: " + user.getStatus());
        }

        // 4. 获取角色列表（只需角色名）
        List<String> roleNames = userRolesRelRepository.getRolesByUserId(user.getUserId())
                .stream().map(RoleDO::getRoleName).collect(Collectors.toList());

        // 5. 生成双令牌（包含用户ID）
        String accessToken = jwtUtils.generateAccessToken(user.getAccount(), roleNames);
        String refreshToken = jwtUtils.generateRefreshToken(user.getAccount());

        // 6. 数据库存入refreshToken信息
        //saveRefreshToken(user.getUserId(), refreshToken, request);

        return AuthResponse.builder()
                .accessToken(accessToken).refreshToken(refreshToken)
                .expiresIn(jwtUtils.getExpirationFromToken(accessToken))
                .tokenType("Bearer").account(user.getAccount())
                .userAvatar(user.getUserAvatar())
                .build();
    }

    private void saveRefreshToken(Long userId, String refreshToken, HttpServletRequest request) {
        //Claims claims = jwtUtils.parseToken(refreshToken);

        RefreshTokenDO refreshTokenDO = RefreshTokenDO.builder()
                .userId(userId)
                .deviceFingerprint(generateDeviceFingerprint(request))
                .refreshHashToken(refreshToken)
                .build();

        refreshTokenRepository.save(refreshTokenDO);
    }

    // 简易设备指纹生成
    private String generateDeviceFingerprint(HttpServletRequest request) {
        String userAgent = request.getHeader("User-Agent");
        String accept = request.getHeader("Accept");
        return DigestUtils.sha256Hex(userAgent + accept);
    }

    @Override
    public RefreshTokenResponse refreshToken(String refreshToken, HttpServletRequest request) {

        // 1. 验证Refresh Token有效性
        try {
            jwtUtils.validateRefreshToken(refreshToken);
        } catch (DomainException e) {
            throw new DomainException(401002, "refreshToken已过期，请重新登陆");
        }


        // 2. 生成新令牌
        String account = jwtUtils.getAccountFromToken(refreshToken);
        UserDO user = userRepository.getUserByAccount(account);
        List<String> roleNames = userRolesRelRepository.getRolesByUserId(user.getUserId())
                .stream().map(RoleDO::getRoleName).collect(Collectors.toList());
        String newAccessToken = jwtUtils.generateAccessToken(account, roleNames);
        String newRefreshToken = jwtUtils.generateRefreshToken(account); // 生成新Refresh Token

        // 3. 数据库更新refreshToken信息
        //saveRefreshToken(user.getUserId(), refreshToken, request);

        return RefreshTokenResponse.builder()
                .accessToken(newAccessToken)
                .refreshToken(newRefreshToken) // 可根据安全策略决定是否返回新Refresh Token
                .expiresIn(jwtUtils.getAccessTokenExpireSeconds())
                .build();
    }

    @Override
    public void logout(String token) {
        String accountFromToken = jwtUtils.getAccountFromToken(token);
        UserDO userDO = userRepository.getUserByAccount(accountFromToken);
        refreshTokenRepository.removeRefreshTokenByUserId(userDO.getUserId());
    }

    private void assignDefaultRole(Long userId) {
        RoleDO defaultRole = roleRepository.selectOne(DEFAULT_ROLE);
        if (defaultRole == null) {
            throw new DomainException(HttpStatus.INTERNAL_SERVER_ERROR.value(), "默认角色未配置");
        }

        UserRolesRelDO rel = new UserRolesRelDO();
        rel.setUserId(userId);
        rel.setRoleId(defaultRole.getRoleId());
        userRolesRelRepository.add(rel);
    }

    private List<RoleVO> getUserRolesWithPermissions(Long userId) {
        List<RoleDO> roles = userRolesRelRepository.getRolesByUserId(userId);
        if (CollectionUtils.isEmpty(roles)) {
            return Collections.emptyList();
        }

        List<Long> roleIds = roles.stream().map(RoleDO::getRoleId).collect(Collectors.toList());
        Map<Long, List<RolePermissionDTO>> permissionsMap = rolePermissionRelRepository
                .getPermissionsByRoleIds(roleIds)
                .stream()
                .collect(Collectors.groupingBy(RolePermissionDTO::getRoleId));

        return roles.stream().map(role -> {
            RoleVO vo = RoleConverter.toVO(role);
            List<RolePermissionDTO> permissions = permissionsMap.getOrDefault(role.getRoleId(), Collections.emptyList());
            vo.setPermissions(PermissionConverter.toVO(permissions));
            return vo;
        }).collect(Collectors.toList());
    }
}
